package main

import (
	"fmt"
	"net/http"
	"net"
	// "os"
	"mode/index"
	"encoding/json"
	"golang.org/x/net/websocket"
	// "github.com/garyburd/redigo/redis"
)

type (
	MonoClient chan<- map[string]string  // 客户端私有信道类型
	RequestData struct {req *http.Request}  // 用户信息结构
	// 私有信道传输模型
	ChannelData struct {
		gid string
		channel MonoClient
	}
	// 用户信息返回类型(RequestData::Get)
	ResultData struct {
		gid string
		uname string
	}
	// Dejson struct {
	// 	uname string
	// 	mess string
	// }
)

var (
	MessChannel = make(chan map[string]string)
	BindChannel = make(chan ChannelData)
	LeaveChannel = make(chan ChannelData)

	KillName = make(map[string]bool)  // 封杀用户列表
)

/**
 * 客户端分组
 * MonoGroup {
 *	 gid1 : {
		 client1 : bool,
		 client2 : bool,
	},
 *	 gid2 : {
		 client1 : bool,
	}
 *	 ...
 * }
 */
var MonoGroup = make(map[string]map[MonoClient]bool)

func main() {
	// 注册长链接
	MonoGroup["10086"] = make(map[MonoClient]bool)
	MonoGroup["10010"] = make(map[MonoClient]bool)
	go MonoBranch()  // 分支操作
	http.Handle("/hicq", websocket.Handler(WsHandler))
	http.HandleFunc("/", index.MainHandle)
	http.HandleFunc("/roomlist", RoomList)
	http.HandleFunc("/kill999", Kill)
	http.HandleFunc("/room", index.RoomHandle)
	listener, _ := net.Listen("tcp", ":8080")
	http.Serve(listener, nil)
}

func Kill(w http.ResponseWriter, req *http.Request) {
	req.ParseForm()
	uname := req.Form["uname"][0]
	KillName[uname] = true
	result := "{\"code\":0}"
	w.Write([]byte(result))
}

func (r RequestData) Get() (ResultData, int8) {
	var gid string
	var uname string
	r.req.ParseForm()
	if _, key := r.req.Form["gid"]; key {
		gid = r.req.Form["gid"][0]
	} else {
		return ResultData {"", ""}, -1
	}
	if _, key := r.req.Form["uname"]; key {
		uname = r.req.Form["uname"][0]
	} else {
		return ResultData {"", ""}, -2
	}
	return ResultData {
		gid: gid,
		uname: uname,
	}, 0
}

/**
 * 多路转接向所有客户端更新状态
 */
 func MonoBranch() {
	for {
		select {
		case pack := <-MessChannel:
			ghash := MonoGroup[pack["gid"]]
			for so := range ghash {
				// 并行发送
				// go func(so MonoClient, pack map[string]string) {
					so <- pack
				// }(so, pack)
			}
		// end case
		case client := <-BindChannel:
			ghash := MonoGroup[client.gid]
			ghash[client.channel] = true
		// end case
		case client := <-LeaveChannel:
			ghash := MonoGroup[client.gid]
			delete(ghash, client.channel)
			close(client.channel)
		// end case
		}
	}
}

/**
 * 维护一个长链接
 */
func WsHandler(ws *websocket.Conn) {
	// 验证
	rdata := RequestData{ws.Request()}  // 用户数据
	data, stat := rdata.Get()  // 用户组
	// 检测传参
	if stat != 0 {
		fmt.Println("参数错误")
		ws.Close()
		return
	}
	// 检测组
	if CheckUserGroup(data.gid) == false {
		fmt.Println("组不存在")
		ws.Close()
		return
	}
	unamelen := len([]rune(data.uname))
	if unamelen == 0 || unamelen > 20 {
		ws.Close()
		return;
	}

	selfChannel := make(chan map[string]string)
	go MonoSend(ws, selfChannel)  // 监听消息信道，发送消息

	// 单用户操作
	BindChannel <- ChannelData{
		gid : data.gid,
		channel: selfChannel,
	}  // 登记

	// 监听
	for {
		if _, key := KillName[data.uname]; key {
			break
		}
		pack := make(map[string]string)
		pack["gid"] = data.gid
		mess := new(string)
		// 获取消息
		if err := websocket.Message.Receive(ws, mess); err != nil {
			break
		}
		// 检测消息
		swaplen := len([]rune(*mess))
		if swaplen == 0 || swaplen > 1024 {
			break
		}
		// 写入消息信道
		var swap = make(map[string]string)
		json.Unmarshal([]byte(*mess), &swap)
		pack["code"] = "0"
		pack["uname"] = swap["uname"]
		pack["mess"] = swap["text"]
		MessChannel <- pack  // 写入消息信道
	}
	// 析构操作
	LeaveChannel <- ChannelData {
		gid: data.gid,
		channel: selfChannel,
	} // 离开
	ws.Close()
	// pack["mess"] = "我下线了~"
	// MessChannel <- pack
}

/**
 * 发送接口
 * 数据格式：hash 表
 * 默认已经包含了 gid, mess
 */
func MonoSend(ws *websocket.Conn, selfChannel chan map[string]string) {
	for mess := range selfChannel {
		body, _ := json.Marshal(mess)
		if err := websocket.Message.Send(ws, string(body)); err != nil {
			break
		}
	}
}

func CheckUserGroup(gid string) bool {
	if _, key := MonoGroup[gid]; key {
		return true
	}
	return false
}

// 请求接口
func RoomList(w http.ResponseWriter, req *http.Request) {
	var result = make([]map[string]string, 0)
	for key, val := range MonoGroup {
		swap := make(map[string]string)
		swap["room"] = key
		swap["len"] = fmt.Sprintf("%d", len(val))
		result =  append(result, swap)
	}
	body, _ := json.Marshal(result)
	fmt.Fprintf(w, "%s", string(body))
}